package com.lakienko.igor.random.values.service.impl;

import com.lakienko.igor.random.values.domain.CategoryRepository;
import com.lakienko.igor.random.values.domain.ValueRepository;
import com.lakienko.igor.random.values.domain.entity.ValueEntity;
import com.lakienko.igor.random.values.rest.convert.RandomsConverters;
import com.lakienko.igor.random.values.service.RandomValuesService;
import com.lakienko.igor.random.values.service.model.in.CategoryIn;
import com.lakienko.igor.random.values.service.model.in.ValueIn;
import com.lakienko.igor.random.values.service.model.out.CategoryOut;
import com.lakienko.igor.random.values.service.model.out.ValueOut;
import jakarta.persistence.EntityNotFoundException;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.UUID;
import java.util.concurrent.ThreadLocalRandom;

@Service
@RequiredArgsConstructor
@Log4j2
public class RandomValuesServiceImpl implements RandomValuesService, RandomsConverters {

    private final ValueRepository valueRepository;

    private final CategoryRepository categoryRepository;

    @Override
    public List<CategoryOut> getAllCategories() {
        return categoryRepository.findAll().stream()
                .map(this::convertCategoryEntityToCategoryOut)
                .toList();
    }

    @Override
    public CategoryOut addNewCategory(CategoryIn categoryIn) {
        return convertCategoryEntityToCategoryOut(categoryRepository.save(convertCategoryInToCategoryOut(categoryIn)));
    }

    @Override
    public CategoryOut updateCategory(CategoryIn categoryIn) {
        return categoryRepository.findById(categoryIn.getId())
                .map(category -> {
                    category.setDescription(categoryIn.getDescription());
                    category.setName(categoryIn.getName());
                    return category;
                })
                .map(categoryRepository::save)
                .map(this::convertCategoryEntityToCategoryOut)
                .orElseThrow(EntityNotFoundException::new);
    }

    @Override
    @Transactional
    public void deleteCategory(UUID categoryId) {
        List<ValueEntity> valueEntities = valueRepository.findValueEntitiesByCategoryId(categoryId);
        valueRepository.deleteAll(valueEntities);

        categoryRepository.deleteById(categoryId);
    }

    @Override
    public ValueOut getValueByCategoryId(UUID categoryId) {
        List<ValueEntity> values = valueRepository.findValueEntitiesByCategoryId(categoryId);

        if (values.isEmpty()) {
            return ValueOut.builder().build();
        }
        int index = getRandomIndex(values);

        return convertCategoryEntityToCategoryOut(values.get(index));
    }

    @Override
    public ValueOut addNewValue(ValueIn valueIn) {
        return categoryRepository.findById(valueIn.getCategoryId())
                .map(category -> valueRepository.save(convertCategoryInToCategoryOut(valueIn, category)))
                .map(this::convertCategoryEntityToCategoryOut)
                .orElseThrow(EntityNotFoundException::new);
    }

    @Override
    public ValueOut updateValue(ValueIn valueIn) {
        return valueRepository.findById(valueIn.getId())
                .map(value -> {
                    value.setDescription(valueIn.getDescription());
                    value.setName(valueIn.getName());
                    return value;
                })
                .map(valueRepository::save)
                .map(this::convertCategoryEntityToCategoryOut)
                .orElseThrow(EntityNotFoundException::new);
    }

    @Override
    @Transactional
    public void deleteValue(UUID valueId) {
        valueRepository.deleteById(valueId);
    }

    private int getRandomIndex(List<?> list) {
        if (list.size() == 1) {
            return 0;
        }

        return ThreadLocalRandom.current().nextInt(0, list.size());
    }

}
